﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using FluentJdf.LinqToJdf;
using FluentJdf.Resources;
using Infrastructure.Core;
using Infrastructure.Core.CodeContracts;
using FluentJdf.Utility;

namespace FluentJdf.Transmission {
    /// <summary>
    /// Configuration of a FileTransmitterFolderInfo
    /// </summary>
    [Serializable]
    public class FileTransmitterFolderInfo : IComparable<FileTransmitterFolderInfo>, IComparable {
        readonly FolderInfoTypeEnum folderInfoType;
        readonly ReadOnlyDictionary<string, string> nameValues;
        readonly int order;
        string destinationFolder;
        string referenceFolder;
        bool suppress;

        /// <summary>
        /// Construct a default FolderInfo for the given part type.  
        /// </summary>
        /// <param name="partType">The type of part (Attachment, Jdf or Jmf).</param>
        public FileTransmitterFolderInfo(FolderInfoTypeEnum partType) :
            this(partType, null, null, -1, null) {}

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="partType">The type of part (Attachment, Jdf or Jmf).</param>
        /// <param name="destinationFolder">The folder where the item will be placed.  If relative, this
        /// folder is relative to the uriBase of the owning FileTransmitterEncoderConfigurationItem.  It
        /// may also be absolute.</param>
        /// <param name="referenceFolder">The folder that will be used in URI references to the item.
        /// If relative, this
        /// folder is relative to the uriBase of the owning FileTransmitterEncoderConfigurationItem.  It
        /// may also be absolute.</param>
        /// <param name="order">Items are placed in order from 0 to n.  If order provided is less than zero, attachments will get
        /// order = 0, JDF will get order = 1 and JMF will get order = 2.</param>
        /// <param name="nameValues">Additional configuration as name/values.</param>
        /// <remarks>
        /// <para>
        /// Both destinationFolder and referenceFolder support the following special values as all or part of the path:
        /// </para>
        /// <para>
        /// {Root} indicates that the path refers to the root of the folder indicated by the FileTransmitterEncoderConfigurationItem's
        /// baseUri property.
        /// </para>
        /// <para>
        /// {Guid" is a unique GUID string generated by the system for each transmission.
        /// </para>
        /// <para>
        /// {Attributes.[attrName]} is an attribute from the root node of the JDF part.  If there is no JDF part,
        /// this will be an empty string.
        /// </para>
        /// </remarks>
        public FileTransmitterFolderInfo(FolderInfoTypeEnum partType, string destinationFolder, string referenceFolder,
                                         int order = 0, IDictionary<string, string> nameValues = null) :
                                             this(partType, destinationFolder, referenceFolder, order, false, nameValues) {}

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="partType">The type of part (Attachment, Jdf or Jmf).</param>
        /// <param name="destinationFolder">The folder where the item will be placed.  If relative, this
        /// folder is relative to the uriBase of the owning FileTransmitterEncoderConfigurationItem.  It
        /// may also be absolute.</param>
        /// <param name="referenceFolder">The folder that will be used in URI references to the item.
        /// If relative, this
        /// folder is relative to the uriBase of the owning FileTransmitterEncoderConfigurationItem.  It
        /// may also be absolute.</param>
        /// <param name="order">Items are placed in order from 0 to n.  If order provided is less than zero, attachments will get
        /// order = 0, JDF will get order = 1 and JMF will get order = 2.</param>
        /// <param name="nameValues">Additional configuration as name/values.</param>
        /// <param name="suppress">True if output of the given item should be suppressed.
        /// JMF parts can be suppressed by simply an item of the type.  Because
        /// JDF items and Attachment items are included by default, you must configure a JDF FolderInfo with
        /// suppress=true to suppress JDF or attachments.</param>
        /// <remarks>
        /// <para>
        /// Both destinationFolder and referenceFolder support the following special values as all or part of the path:
        /// </para>
        /// <para>
        /// {Root} indicates that the path refers to the root of the folder indicated by the FileTransmitterEncoderConfigurationItem's
        /// baseUri property.
        /// </para>
        /// <para>
        /// {Guid" is a unique GUID string generated by the system for each transmission.
        /// </para>
        /// <para>
        /// {Attributes.[attrName]} is an attribute from the root node of the JDF part.  If there is no JDF part,
        /// this will be an empty string.
        /// </para>
        /// </remarks>
        public FileTransmitterFolderInfo(FolderInfoTypeEnum partType, string destinationFolder, string referenceFolder, int order,
                                         bool suppress, IDictionary<string, string> nameValues) {
            ParameterCheck.ParameterRequired(partType, "partType");

            if (destinationFolder == null) {
                destinationFolder = "${Root}";
            }
            if (referenceFolder == null) {
                referenceFolder = "${Root}";
            }

            this.suppress = suppress;
            this.destinationFolder = destinationFolder.EnsureTrailingSlash();
            this.referenceFolder = referenceFolder.EnsureTrailingSlash();
            this.order = order;

            this.nameValues = new ReadOnlyDictionary<string, string>(nameValues ?? new Dictionary<string, string>());

            switch (partType) {
                case FolderInfoTypeEnum.Attachment:
                case FolderInfoTypeEnum.Jdf:
                case FolderInfoTypeEnum.Jmf:
                    folderInfoType = partType;
                    break;
                default:
                    throw new JdfException(string.Format(Messages.PartTypeMustBeAttachmentJdfOrJmfTheTypeGivenForFolderInfo, partType));
            }

            if (order < 0) {
                this.order = (int) folderInfoType;
            }
        }

        /// <summary>
        /// Gets the destination folder.  This is where the file
        /// will be placed by the file transmitter.
        /// </summary>
        public string DestinationFolder {
            get { return destinationFolder; }
            set { destinationFolder = value; }
        }

        /// <summary>
        /// Gets the type of part this info applies to.
        /// </summary>
        public FolderInfoTypeEnum FolderInfoType {
            get { return folderInfoType; }
        }

        /// <summary>
        /// Additional Name Values
        /// </summary>
        public ReadOnlyDictionary<string, string> NameValues {
            get { return nameValues; }
        }

        /// <summary>
        /// Gets the order for transmission.  Transmission order
        /// for items with identical Order is not defined.
        /// </summary>
        public int Order {
            get { return order; }
        }

        /// <summary>
        /// Returns true if this type of item
        /// should not be output.
        /// </summary>
        public bool Suppress {
            get { return suppress; }
            set { suppress = value; }
        }

        /// <summary>
        /// Gets the rerfence folder.  This is the path
        /// that will be used in all references located in the 
        /// JDF and JMF.  
        /// </summary>
        public string ReferenceFolder {
            get { return referenceFolder; }
            set { referenceFolder = value; }
        }

        #region IComparable Members

        /// <summary>
        /// Compare the order of this item with the order of another.
        /// </summary>
        /// <param name="obj">FileTransmitterFolderInfo to which to compare</param>
        /// <returns>0 if equals or if obj is not a non-null FileTransmitterFolderInfo. 1 if this is greater, -1 if this is less than.</returns>
        public int CompareTo(object obj) {
            if (obj is FileTransmitterFolderInfo) {
                return order.CompareTo(((FileTransmitterFolderInfo) obj).Order);
            }
            else {
                return 0;
            }
        }

        #endregion

        #region IComparable<FileTransmitterFolderInfo> Members

        /// <summary>
        /// Compare the order of this item with the order of another.
        /// </summary>
        /// <param name="other">FileTransmitterFolderInfo to which to compare</param>
        /// <returns>0 if equals or if obj is not a non-null FileTransmitterFolderInfo. 1 if this is greater, -1 if this is less than.</returns>
        public int CompareTo(FileTransmitterFolderInfo other) {
            if (other == null) {
                return 0;
            }
            return order.CompareTo(other.Order);
        }

        #endregion

        /// <summary>
        /// Return a string representation of the object.
        /// </summary>
        public override string ToString() {
            var sb = new StringBuilder();
            sb.AppendFormat("Destination Folder: {0} Reference Folder: {1} Order: {2} FolderInfoType: {3} Suppress: {4}",
                            destinationFolder, referenceFolder, order, folderInfoType.ToString(), suppress);
            return sb.ToString();
        }
    }
}